package cn.blnp.net.flowable.transfer.gateway.inclusive;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <h3>网关-包容网关</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/19 17:07
 */
@Slf4j
@SpringBootTest
public class InclusiveGatewayTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    /**
     *  <h2>包容网关</h2>
     *  <p>
     *  &emsp;&emsp;包容网关可以看作排他网关和并行网关的结合体。与排他网关一样，我们可以在外出顺序流上定义条件。但与排他网关有所不同的是，进行决策判断时，包容网关所有条件为true的后继分支都会被依次执行。
     *  如果所有分支条件决策都为false且该网关定义了一个默认分支，那么该默认分支将被执行。如果没有可执行的分支，则会抛出异常（流程设计上应避免这种情况发生）。包容网关同样包含分支和合并两种行为。
     *  </p>
     *  <p>
     *   &emsp;&emsp;包容网关允许多进多出。如果同一个包容节点有多条进入和外出顺序流，那么它会同时具备分支和合并功能。网关会先合并所有“可以到达”包容网关的进入顺序流，再根据条件判断结果为true的外出顺序流，
     *   为它们生成多个并行分支。<br/>
     *      <b>流程定义文件：</b>{@link flow/inclusiveGatewayProcess.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://cdn4.winhlb.com/2025/05/19/682af8b062343.png" />
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("包容网关-演示案例")
                .addClasspathResource("flow/inclusiveGatewayProcess.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());

        //查询请假申请任务
        Task applyTask = taskQuery.singleResult();
        log.info("流程发起后，第一个用户任务名称为：{}", applyTask.getName());
        //设置leaveDays变量值
        Map<String, Object> varMap = MapUtil.builder(new HashMap<String, Object>())
                .put("leaveDays", 5)
                .build();
        //完成请假申请任务
        taskService.complete(applyTask.getId(), varMap);
        List<Task> taskList1 = taskQuery.list();
        //查询任务个数
        log.info("第一个任务办理后，当前流程的任务个数为{}，分别为：{}", taskList1.size(),
                taskList1.stream().map(Task::getName).collect(Collectors.joining("，")));
        //办理任务
        taskList1.stream().forEach(task -> {
            taskService.complete(task.getId());
            log.info("办理完成用户任务：{}", task.getName());
        });
        List<Task> taskList2 = taskQuery.list();
        //查询任务个数
        log.info("办理完前{}个任务后，流程当前任务个数为{}，分别为：{}", taskList1.size(),
                taskList2.size(),
                taskList2.stream().map(Task::getName)
                        .collect(Collectors.joining("，")));
        //办理任务
        taskList2.stream().forEach(task -> {
            taskService.complete(task.getId());
            log.info("办理完成用户任务：{}", task.getName());
        });
        //查询剩余任务个数
        log.info("最后剩余的用户任务数为{}", taskQuery.list().size());

        /**
         *  演示结果：
         *  2025-05-19 17:26:17.393  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 部署流程定义：50a1c0a3-3493-11f0-8db1-50ebf6ec8f6d
         * 2025-05-19 17:26:17.398  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 流程定义编号：inclusiveGatewayProcess:1:51120ea6-3493-11f0-8db1-50ebf6ec8f6d
         * 2025-05-19 17:26:17.498  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 流程发起后，第一个用户任务名称为：请假申请
         * 2025-05-19 17:26:17.581  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 第一个任务办理后，当前流程的任务个数为2，分别为：HR助理审批，直属领导审批
         * 2025-05-19 17:26:17.610  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 办理完成用户任务：HR助理审批
         * 2025-05-19 17:26:17.639  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 办理完成用户任务：直属领导审批
         * 2025-05-19 17:26:17.642  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 办理完前2个任务后，流程当前任务个数为1，分别为：HR经理审批
         * 2025-05-19 17:26:17.695  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 办理完成用户任务：HR经理审批
         * 2025-05-19 17:26:17.697  INFO 58688 --- [           main] c.b.n.f.t.g.i.InclusiveGatewayTest       : 最后剩余的用户任务数为0
         **/
    }
}
